#include "batterymonitor.h"
#include "rpi.h"

#include <stdio.h>
#include "stdlib.h"
#include <string>
#include <boost/thread.hpp>
#include <boost/date_time.hpp>
#include "dirent.h"
#include "sys/stat.h"


#define ADC_ADDRESS ( 0x2A  )

#define ADC_PU_CTRL  0x00
#define ADC_CTRL1    0x01
#define ADC_CTRL2    0x02
#define ADC_OCAL1_B2 0x03
#define ADC_OCAL1_B1 0x04
#define ADC_OCAL1_B0 0x05
#define ADC_GCAL1_B3 0x06
#define ADC_GCAL1_B2 0x07
#define ADC_GCAL1_B1 0x08
#define ADC_GCAL1_B0 0x09
#define ADC_OCAL2_B2 0x0A
#define ADC_OCAL2_B1 0x0B
#define ADC_OCAL2_B0 0x0C
#define ADC_GCAL2_B3 0x0D
#define ADC_GCAL2_B2 0x0E
#define ADC_GCAL2_B1 0x0F
#define ADC_GCAL2_B0 0x10
#define ADC_I2C      0x11
#define ADC_ADCO_B2  0x12
#define ADC_ADCO_B1  0x13
#define ADC_ADCO_B0  0x14
#define ADC_OTP_B1   0x15
#define ADC_ITP_B0   0x16
#define ADC_WHO_AM_I 0x1F

#define ADC_VOLTAGE_OFFSET     -1.146521
#define ADC_VOLTAGE_CALIBRATION 12.5534 * 1.121118
#define ADC_CURRENT_OFFSET      0.5487581
#define ADC_CURRENT_CALIBRATION 65.0546133


#define BM_DIRECTORY "./battery"




BatteryMonitor::BatteryMonitor(int file) : I2CDevice(file, ADC_ADDRESS)
{
  __s32 res;
  __u8  reg, val;


  reg = ADC_PU_CTRL;
  // b00000110
  val = 0x06 ;
  writeByte(reg, val);



  reg = ADC_CTRL1;
  // b00000001   gain 1
  val = 0x01;
  writeByte(reg, val);

      //writeByte(ADC_CTRL2, 0x00);
  reg = ADC_I2C;
  val = 0x00;
  writeByte(reg, val);

  reg = ADC_CTRL2;
  res = readByte(reg);
  if ((res & 0x04) != 0)
  {
    printf("Battery Monitor: Calibration error\n");
    exit(1);
  }

  RPi::setupInterrupt(RPI_12, RPi::Rising);

  
  // *********************************
  // -- read in the battery data file --


  fh = 0;
  char filename[256];

  // check if ``battery'' directory exists
  struct stat st; 

  bool file_opened = true;
  int num = 0;
  if (stat(BM_DIRECTORY, &st) != 0)
  {
    printf("(no directory)\n");
    fflush(stdout);
    // directory does not exist
    if (mkdir(BM_DIRECTORY, S_IRWXU | S_IRWXG | S_IRWXO) != 0)
    {
      printf("BatteryMonitor: failed to create battery directory\n");
      exit(1);
    }
    file_opened = false;
    num = 0;

    printf("BatteryMonitor: New Directory Created.\n\n");
  }
  else
  {

    fflush(stdout);
    struct dirent **namelist;
    int n;

    n = scandir(BM_DIRECTORY, &namelist, 0, alphasort);
    printf("%d\n", n);
    fflush(stdout);
    if (n < 0)
    {
      printf("BatteryMonitor: scan dir failed\n");
      exit(1);
    }
    else if (n > 2)
    {   
      sprintf(filename, "%s/%s", BM_DIRECTORY, namelist[n-1]->d_name);
      printf("%s\n\n", filename);
      printf("before pointer:%p\n", fh);
      fh = (void*)fopen(filename, "a+"); //open file for reading and appending
      printf("after pointer:%p\n", fh);
      if ((fh == NULL) || (RPi::hasOption("nb")))
      {
        file_opened = false;
        num = 0;
        if (sscanf(filename, "%d.dat", &num) != 1)
        {
          num = 0;
        }
        else
        {
          num++;
        }
      }
      else
      {
        // read the values from the file
        


        if (fseek((FILE*)(fh), -12, SEEK_END) != 0)
        {
          printf("BatteryMonitor: file %s seems empty. Reseting total charge to 0.\n", filename);
          m_total_charge_drawn = 0;
        }
        else
        {
          char buf[100];
          for (int k = 0; k < 100; k++)
            buf[k] = 0;

          fread(buf, 100, 1, (FILE*)fh);
          printf("read: %s\n", buf);
          float temp = 0;
          sscanf(buf, "%f", &temp);
          m_total_charge_drawn = temp;
        }
      }

    } // end of 
    else
    {
      num = 0;
      file_opened = false;
    }
  }


  if (!file_opened)
  {
    sprintf(filename, "%s/%04d.dat", BM_DIRECTORY, num);
    printf("BatteryMonitor: battery file not opened, creating new with name %s\n", filename);

    fh = (void*)fopen(filename, "a");
    if (fh == NULL)
    {
      printf("BatteryMonitor: creating of new file %s failed.\n", filename);

    }

    m_total_charge_drawn = 0;
  }



  m_voltage = 0;
  m_current = 0;




  // *********************************
  // start the battery monitor thread

  m_thread = (void*) new boost::thread(boost::bind(&BatteryMonitor::thread_loop, this));

}


/*
void split( vector<string> & theStringVector,  
       const  string  & theString,
       const  string  & theDelimiter)
{

    size_t  start = 0, end = 0;

    while ( end != string::npos)
    {
        end = theString.find( theDelimiter, start);

        // If at end, use length=maxLength.  Else use length=end-start.
        theStringVector.push_back( theString.substr( start,
                       (end == string::npos) ? string::npos : end - start));

        // If at end, use start=maxSize.  Else use start=end+delimiter.
        start = (   ( end > (string::npos - theDelimiter.size()) )
                  ?  string::npos  :  end + theDelimiter.size());
    }
}
*/


double BatteryMonitor::readVoltage()
{
  __u8 b2, b1, b0;

  b2 = readByte(ADC_ADCO_B2);
  b1 = readByte(ADC_ADCO_B1);
  b0 = readByte(ADC_ADCO_B0);

  __u8 sign = 0x00;
  if ((0x80 & b2) != 0)
    sign = 0xFF;

  __s32 val = ( (b2 << 16) | (b1 << 8) | b0 | sign << 24);
  //printf("0x00%02X%02X%02X, 0x%08X, %d\n", b2, b1, b0, val, val);

  return val / 8388608.0;
}



void BatteryMonitor::thread_loop()
{
  printf("i'm in my loop\n");

  int ret;
  int id = 0;
  long last = RPi::msec();
  while (true)
  {
    ret = RPi::pollInterrupt(RPI_12, 1000); 
    //printf("time since last: %d\n", RPi::usec() - last);
    //last = RPi::usec();
    if (ret == -1) 
    {
      printf("poll error in battery monitor\n");
      continue;
    }
    if (ret ==  -2) 
    {
      printf("timeout\n");
      continue;
    }
    double v = readVoltage();

    if (id == 6)
    {
      m_voltage = v * ADC_VOLTAGE_CALIBRATION - ADC_VOLTAGE_OFFSET;
      printf("v: %d %.7f\n", id, m_voltage);
    }
    else if (id == 0)
    {
      m_current = (fabs(v) - ADC_CURRENT_OFFSET) * ADC_CURRENT_CALIBRATION;
      printf("i: %d %.7f\n", id, m_current);
      long now = RPi::msec();
      int t = now - last;

  m_total_charge_drawn += m_current * t/60/60;
      fprintf((FILE*)fh, "%6.3f %6.3f %11.6f\n", m_current, m_voltage, m_total_charge_drawn);
      fflush((FILE*)fh);

    }



    if (id == 0)
      writeByte(ADC_CTRL2, 0x80);
    else if (id == 6) 
      writeByte(ADC_CTRL2, 0x00);

    id++;
    id = id % 12;
  }
}
